"""
doc
"""

import logging
from datetime import datetime
from django.utils import timezone
from django.views import View
from django.http import HttpRequest
from django.db import transaction
from django.utils.decorators import method_decorator

from rest_framework.parsers import JSONParser

from app_common import (
    app_page,
    app_except,
    app_str_check,
    try_and_need_login_decorators,
    json_response_utf8_ok,
)

from . import models

from . import ser

logger = logging.getLogger("log")


# Create your views here.
@method_decorator(try_and_need_login_decorators, name="dispatch")
class CommunityView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        _objs = models.Community.objects.filter(deleted=False)
        _objs, num_pages = app_page.page(request, _objs)
        m_s = ser.CommunitySer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data, "num_pages": num_pages})

    @transaction.atomic
    def post(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        community_name = body["community_name"]
        app_str_check.check_name(community_name)

        models.Community.objects.select_for_update()
        if (
            models.Community.objects.filter(deleted=False)
            .filter(community_name=community_name)
            .exists()
        ):
            raise app_except.NameExist

        m = models.Community(**body)
        m.save()

        m_s = ser.CommunitySer(m)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def put(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        community_uuid = body.pop("uuid")

        community_name = body.get("community_name", None)
        if community_name:
            app_str_check.check_name(community_name)

        m = (
            models.Community.objects.filter(deleted=False)
            .filter(uuid=community_uuid)
            .select_for_update()
            .first()
        )
        if not m:
            raise app_except.NameNotExist

        m_s = ser.CommunitySer(m, data=body, partial=True)
        m_s.is_valid(raise_exception=True)
        m_s.save()

        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def delete(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        _list = body.pop("list")

        models.Community.objects.select_for_update()

        n = (
            models.Community.objects.filter(deleted=False)
            .filter(uuid__in=_list)
            .update(deleted=True)
        )
        if n == 0:
            raise app_except.ObjectNotExist

        return json_response_utf8_ok({"data": f"del count: {n}"})


@method_decorator(try_and_need_login_decorators, name="dispatch")
class HouseView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        community_uuid = request.GET.get("community_uuid")
        overdue = request.GET.get("overdue", None)
        if not community_uuid:
            raise app_except.NullErr

        _objs = models.House.objects.filter(deleted=False).filter(
            community_uuid=community_uuid
        )

        if overdue:
            _objs = _objs.filter(paid_at__lte=timezone.now())

        _objs, num_pages = app_page.page(request, _objs)
        m_s = ser.HouseSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data, "num_pages": num_pages})

    @transaction.atomic
    def post(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        _list = body.pop("list")

        models.House.objects.select_for_update()

        _bulk = []
        for _obj in _list:
            if (
                models.House.objects.filter(deleted=False)
                .filter(community_uuid=_obj["community_uuid"])
                .filter(building_num=_obj["building_num"])
                .filter(unit_num=_obj["unit_num"])
                .filter(house_num=_obj["house_num"])
                .exists()
            ):
                raise app_except.NameExist

            _bulk.append(models.House(**_obj))

        _objs = models.House.objects.bulk_create(_bulk, batch_size=1000)
        m_s = ser.HouseSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def put(self, request: HttpRequest, *args, **kwargs):
        def check_datetime_str(s: str) -> None:
            time_str = s.strip()
            _time: datetime = timezone.datetime.fromisoformat(time_str)
            if (
                _time.hour != 0
                or _time.minute != 0
                or _time.second != 0
                or _time.microsecond != 0
            ):
                raise app_except.ValueErr

        body: dict = JSONParser().parse(request)
        _list = body.pop("list")

        models.House.objects.select_for_update()

        _bulk = []
        attrs = ["area_size", "paid_at"]

        for _obj in _list:
            house_uuid = _obj.pop("uuid")

            m = (
                models.House.objects.filter(deleted=False)
                .filter(uuid=house_uuid)
                .first()
            )
            if not m:
                raise app_except.ObjectNotExist

            change = False
            for _key in attrs:
                if _key not in _obj:
                    continue
                if _key == "paid_at":
                    check_datetime_str(_obj[_key])
                setattr(m, _key, _obj[_key])
                change = True

            if not change:
                raise app_except.AttrNoChange

            _bulk.append(m)

        models.House.objects.bulk_update(_bulk, fields=attrs, batch_size=1000)
        m_s = ser.HouseSer(_bulk, many=True)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def delete(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        _list = body.pop("list")

        models.House.objects.select_for_update()

        n = (
            models.House.objects.filter(deleted=False)
            .filter(uuid__in=_list)
            .update(deleted=True)
        )
        if n == 0:
            raise app_except.ObjectNotExist

        return json_response_utf8_ok({"data": f"del count: {n}"})


@method_decorator(try_and_need_login_decorators, name="dispatch")
class HouseBillView(View):
    def get(self, request: HttpRequest, *args, **kwargs):
        state_values = request.GET.get("state_values", None)
        if state_values:
            return json_response_utf8_ok({"data": models.HouseBill.BillState.values})

        state = request.GET.get("state", None)
        house_uuid = request.GET.get("house_uuid", None)
        _objs = models.HouseBill.objects.filter(deleted=False)
        if house_uuid:
            _objs = _objs.filter(house_uuid=house_uuid)
        if state:
            _objs = _objs.filter(state=state)
        _objs, num_pages = app_page.page(request, _objs)
        m_s = ser.HouseBillSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data, "num_pages": num_pages})

    @transaction.atomic
    def post(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        _list = body.pop("list")
        if not _list:
            raise app_except.NullErr

        _objs = models.batch_create_house_bill(
            (models.HouseBill(**obj) for obj in _list)
        )
        m_s = ser.HouseBillSer(_objs, many=True)
        return json_response_utf8_ok({"data": m_s.data})

    @transaction.atomic
    def delete(self, request: HttpRequest, *args, **kwargs):
        body: dict = JSONParser().parse(request)
        _list = body.pop("list")

        models.HouseBill.objects.select_for_update()
        n = (
            models.HouseBill.objects.filter(deleted=False)
            .filter(uuid__in=_list)
            .update(deleted=True)
        )
        if n == 0:
            raise app_except.ObjectNotExist

        return json_response_utf8_ok({"data": f"del count: {n}"})
